﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Zinnia.Logging;

namespace Zinnia.Caching.Imp
{
    /// <summary>
    /// 默认的异步令牌提供者
    /// </summary>
    public class DefaultAsyncTokenProvider : IAsyncTokenProvider
    {
        public DefaultAsyncTokenProvider()
        {
            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public IVolatileToken GetToken(Action<Action<IVolatileToken>> task)
        {
            var token = new AsyncVolativeToken(task, Logger);
            token.QueueWorkItem();
            return token;
        }

        /// <summary>
        /// 异步Volative令牌
        /// </summary>
        class AsyncVolativeToken : IVolatileToken
        {
            private readonly Action<Action<IVolatileToken>> _task;
            private readonly List<IVolatileToken> _taskTokens = new List<IVolatileToken>();
            private volatile Exception _taskException;
            private volatile bool _isTaskFinished;

            public AsyncVolativeToken(Action<Action<IVolatileToken>> task, ILogger logger)
            {
                _task = task;
                Logger = logger;
            }
            /// <summary>
            /// 日志记录器
            /// </summary>
            public ILogger Logger { get; set; }
            /// <summary>
            ///  队列工作项
            /// </summary>
            public void QueueWorkItem()
            {
                // 开启一个工作项的时候收集token到一个内部数组_taskTokens中
                ThreadPool.QueueUserWorkItem(state =>
                {
                    try
                    {
                        _task(token => _taskTokens.Add(token));
                    }
                    catch (Exception e)
                    {
                        Logger.Error(e, "Error while monitoring extension files. Assuming extensions are not current.");
                        _taskException = e;
                    }
                    finally
                    {
                        _isTaskFinished = true;
                    }
                });
            }
            
            public bool IsCurrent
            {
                get
                {
                    // We are current until the task has finished
                    if (_taskException != null)
                    {
                        return false;
                    }
                    
                    if (_isTaskFinished)
                    {
                        return _taskTokens.All(t => t.IsCurrent);
                    }
                    return true;
                }
            }
        }
    }
}
